/*
 * @Description: 
 * @FilePath: /STM32F103/USER/bsp/spi.c
 * @Author: Wei Zhou
 * @Github: https://github.com/zromyk
 * @Date: 2021-04-28 15:25:19
 * @LastEditors: Wei Zhou
 * @LastEditTime: 2021-05-12 22:50:58
 * @Copyright: Copyright  2017 muyiro. All rights reserved.
 */

#include "spi.h"
#include "nvic.h"
#include "rcc.h"

uint8_t TxBuffer_SPI1[TxBuffer_Size_SPI1];
uint8_t RxBuffer_SPI1[RxBuffer_Size_SPI1];

uint8_t TxBuffer_SPI2[TxBuffer_Size_SPI2];
uint8_t RxBuffer_SPI2[RxBuffer_Size_SPI2];

SPI_Dev spi1 =
{
    .SPIx = SPI1,

    /*
                    0       1
        SPI1_NSS    PA4     --PA15--
        SPI1_SCK    PA5     --PB3--
        SPI1_MISO   PA6     --PB4--
        SPI1_MOSI   PA7     --PB5--
    */
    .RCC_Periph_SPI     = RCC_APB2Periph_SPI1,
    .pin_nss            = PA4,
    .pin_sck            = PA5,
    .pin_miso           = PA6,
    .pin_mosi           = PA7,

    .tx_buf             = TxBuffer_SPI1,
    .rx_buf             = RxBuffer_SPI1,
    .tx_buf_size        = TxBuffer_Size_SPI1,
    .rx_buf_size        = RxBuffer_Size_SPI1,
    .rx_cnt             = 0,

    .SPIx_IRQn          = SPI1_IRQn,

    /*              
        SPI1_TX     DMA1
                    channel 3
                    
        SPI1_RX     DMA1
                    channel 2
    */
    .dma.DMAx           = DMA1,
    .dma.RCC_Periph_DMA = RCC_AHBPeriph_DMA1,

    .dma.tx_channel     = DMA1_Channel3,
    .dma.tx_FLAG_TC     = DMA1_FLAG_TC3,

    .dma.rx_channel     = DMA1_Channel2,
    .dma.rx_FLAG_TC     = DMA1_FLAG_TC2};

SPI_Dev spi2 =
{
    .SPIx = SPI2,

    /*
                    0
        SPI2_NSS    PB12
        SPI2_SCK    PB13
        SPI2_MISO   PB14
        SPI2_MOSI   PB15
    */
    .RCC_Periph_SPI     = RCC_APB1Periph_SPI2,
    .pin_nss            = PB12,
    .pin_sck            = PB13,
    .pin_miso           = PB14,
    .pin_mosi           = PB15,

    .tx_buf             = TxBuffer_SPI2,
    .rx_buf             = RxBuffer_SPI2,
    .tx_buf_size        = TxBuffer_Size_SPI2,
    .rx_buf_size        = RxBuffer_Size_SPI2,
    .rx_cnt             = 0,

    .SPIx_IRQn          = SPI2_IRQn,

    /*              
        SPI2_TX     DMA1
                    channel 5
                    
        SPI2_RX     DMA1
                    channel 4
    */
    .dma.DMAx           = DMA1,
    .dma.RCC_Periph_DMA = RCC_AHBPeriph_DMA1,

    .dma.tx_channel     = DMA1_Channel5,
    .dma.tx_FLAG_TC     = DMA1_FLAG_TC5,

    .dma.rx_channel     = DMA1_Channel4,
    .dma.rx_FLAG_TC     = DMA1_FLAG_TC4
};

static void gpio_configuration(SPI_Dev *spi, SPI_Master_Slave master_slave)
{
    if (master_slave == Master) {
        gpio_af_init(spi->pin_sck);
        gpio_af_init(spi->pin_mosi);
        gpio_in_init(spi->pin_miso, GPIO_PuPd_NOPULL); 
    }
    else if (master_slave == Slave) {
        gpio_in_init(spi->pin_sck,  GPIO_PuPd_NOPULL);
        gpio_in_init(spi->pin_mosi, GPIO_PuPd_NOPULL);
        gpio_af_init(spi->pin_miso); 
    }
}

static void spi_configuration(SPI_Dev *spi, SPI_Master_Slave master_slave, SPI_Mode mode)
{
    SPI_InitTypeDef SPI_InitStruct;

    rcc_periph_clock_enable(spi->SPIx, spi->RCC_Periph_SPI);

    /* SPI1 Config -------------------------------------------------------------*/
    /* Notes.  As far as I can tell, the AVR numbers for dataMode appear to match the numbers required by the STM32

    CPOL    CPHA    Shift SCK-edge  Capture SCK-edge
    0       0       Falling         Rising
    0       1       Rising          Falling
    1       0       Rising          Falling
    1       1       Falling         Rising

    On the STM32 it appears to be

    bit 1 - CPOL : Clock polarity
        (This bit should not be changed when communication is ongoing)
        0 : CLK to 0 when idle
        1 : CLK to 1 when idle

    bit 0 - CPHA : Clock phase
        (This bit should not be changed when communication is ongoing)
        0 : The first clock transition is the first data capture edge
        1 : The second clock transition is the first data capture edge
     
    If someone finds this is not the case or sees a logic error with this let me know ;-) 
    */

    SPI_InitStruct.SPI_Direction    = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode         = master_slave; /* SPI_Mode */
    SPI_InitStruct.SPI_DataSize     = SPI_DataSize_8b; /* 8 bit */
    switch(mode)
    {
        case Mode0_CPOL0_CPHA0:
            SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
            SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
            break;
        case Mode1_CPOL0_CPHA1:
            SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
            SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
            break;
        case Mode2_CPOL1_CPHA0:
            SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
            SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
            break;
        case Mode3_CPOL1_CPHA1:
            SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
            SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
            break;
    }
    SPI_InitStruct.SPI_NSS                  = SPI_NSS_Soft; /* soft ss */
    SPI_InitStruct.SPI_BaudRatePrescaler    = SPI_BaudRatePrescaler_16;
    SPI_InitStruct.SPI_FirstBit             = SPI_FirstBit_MSB;
    SPI_InitStruct.SPI_CRCPolynomial        = 7;

    SPI_Init(spi->SPIx, &SPI_InitStruct);
}

void spi_init(SPI_Dev *spi, SPI_Master_Slave master_slave, SPI_Mode mode)
{
    gpio_configuration(spi, master_slave);
    spi_configuration(spi, master_slave, mode);
    SPI_CalculateCRC(spi->SPIx, ENABLE);

    /* Enable the SPIx */
    SPI_Cmd(spi->SPIx, ENABLE);
}

void spi_user_define_init(SPI_Dev *spi, SPI_InitTypeDef *SPI_InitStruct)
{
    gpio_configuration(spi, (SPI_Master_Slave)SPI_InitStruct->SPI_Mode);
    rcc_periph_clock_enable(spi->SPIx, spi->RCC_Periph_SPI);
    SPI_Init(spi->SPIx, SPI_InitStruct);
    SPI_CalculateCRC(spi->SPIx, ENABLE);

    /* Enable the SPIx */
    SPI_Cmd(spi->SPIx, ENABLE);
}

void spi_it_rx_init(SPI_Dev *spi, uint8_t priority, uint8_t subPriority)
{
    nvic_configuration(spi->SPIx_IRQn, priority, subPriority);
    /* Enable SPI_MASTER TXE interrupt */
    /* SPI_I2S_ITConfig(spi->SPIx, SPI_I2S_IT_TXE, ENABLE); */
    /* Enable SPI_SLAVE RXNE interrupt */
    SPI_I2S_ITConfig(spi->SPIx, SPI_I2S_IT_RXNE, ENABLE);
}

/**
  * @brief  Configures DMA of SPI_Dev.
  * @param  spi: SPIx_Dev where x can be 1, 2, 3 ... to select the SPI peripheral.
  * @retval None
  */
void spi_dma_rx_init(SPI_Dev *spi)
{
    dma_rx_init(&spi->dma, (uint32_t)&spi->SPIx->DR, spi->rx_buf, spi->rx_buf_size);

    SPI_I2S_DMACmd(spi->SPIx, SPI_I2S_DMAReq_Rx, ENABLE);
    DMA_Cmd(spi->dma.rx_channel, ENABLE);
}

void spi_dma_tx_init(SPI_Dev *spi)
{
    dma_tx_init(&spi->dma, (uint32_t)&spi->SPIx->DR, spi->tx_buf, spi->tx_buf_size);

    SPI_I2S_DMACmd(spi->SPIx, SPI_I2S_DMAReq_Tx, ENABLE);
    DMA_Cmd(spi->dma.rx_channel, ENABLE);
}

void spi_dma_tx_size_enable(SPI_Dev *spi, uint32_t size)
{
    while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
        ;
    DMA_Cmd(spi->dma.tx_channel, DISABLE);
    DMA_SetCurrDataCounter(spi->dma.tx_channel, size);
    DMA_Cmd(spi->dma.tx_channel, ENABLE);
}

void spi_dma_tx_enable(SPI_Dev *spi)
{
    spi_dma_tx_size_enable(spi, spi->tx_buf_size);
}

void spi_write_byte(SPI_Dev *spi, const uint8_t byte)
{
    SPI_I2S_SendData(spi->SPIx, byte);
    while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
        ;
}

uint8_t spi_read_byte(SPI_Dev *spi)
{
    SPI_I2S_SendData(spi->SPIx, 0x00);
    while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
        ;
    while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_RXNE) == RESET)
        ;
    return SPI_I2S_ReceiveData(spi->SPIx);
}

void spi_write_buffer(SPI_Dev *spi, const uint8_t *buf, uint32_t length)
{
    while (length--) {
        SPI_I2S_SendData(spi->SPIx, *buf++);
        while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
            ;
    }
}

/* Reads and sends a byte array */
void spi_transfer(SPI_Dev *spi, uint8_t *tx, uint8_t *rx, uint32_t length)
{
    while (length--) {
        SPI_I2S_SendData(spi->SPIx, *tx++);
        while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
            ;
        while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_RXNE) == RESET)
            ;
        *rx++ = SPI_I2S_ReceiveData(spi->SPIx);
    }
}
